package admin

import (
	"config-client-v1/init/global"
	j_log "config-client-v1/init/j-log"
	"config-client-v1/model"
	"config-client-v1/utils/tools"
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/gorilla/websocket"
	"gorm.io/gorm"
	"strconv"
	"strings"
)

type VariableController struct {
}

func (con VariableController) List(ctx *gin.Context) {
	userToken := GetUserToken(ctx)
	param := struct {
		FSID        int    `json:"fs_id"`
		ModuleID    int    `json:"module_id"`
		PPEID       int    `json:"ppe_id"`
		FrameID     int    `json:"frame_id"`
		FieldName   string `json:"field_name" binding:"max=20"`
		Addr        string `json:"addr" binding:"max=20"`
		Search      string `json:"search" binding:"max=20"`
		SpecType    string `json:"spec_type" binding:"max=20"`
		ModuleType  string `json:"module_type" binding:"max=20"`
		ChannelType string `json:"channel_type" binding:"max=20"`
		Custom      string `json:"custom" binding:"max=20"`
		ValueType   string `json:"value_type" binding:"max=20"`
		model.QueryData
	}{}
	err := ctx.ShouldBindBodyWith(&param, binding.JSON)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的参数",
			"data":    err.Error(),
		})
		return
	}

	tx := model.DB.Model(&model.VariableModel{})
	tx.Where("project_id = ?", userToken.ProjectID)

	if param.FSID != 0 {
		tx.Where("fs_id = ?", param.FSID)
	}
	if param.ModuleID != 0 {
		tx.Where("module_id = ?", param.ModuleID)
	}
	if param.PPEID != 0 {
		tx.Where("ppe_id = ?", param.PPEID)
	}
	if param.FrameID != 0 {
		tx.Where("frame_id = ?", param.FrameID)
	}
	if param.FieldName != "" {
		tx.Where("field_name like ?", "%"+param.FieldName+"%")
	}
	if param.Addr != "" {
		tx.Where("addr like ?", "%"+param.Addr+"%")
	}
	if param.Search != "" {
		tx.Where("field_name like ? OR addr like ?", "%"+param.Search+"%", "%"+param.Search+"%")
	}

	switch param.Custom {
	case "ALL_FS":
		tx.Where("fs_id > 0")
	case "ALL_PPE":
		tx.Where("ppe_id > 0")
	case "ALL_INTERNAL":
		tx.Where("code1 = '90'")
	}
	if param.ModuleType != "" {
		code2, ok := model.AllowFSCode2[param.ModuleType]
		if !ok {
			ctx.JSON(200, gin.H{
				"code":    2,
				"message": "无效的参数 parent_module_type",
				"data": map[string]interface{}{
					"module_type可选范围": model.AllowFSCode2,
				},
			})
			return
		}
		tx.Where("fs_id > 0 AND code2 = ?", code2)
	}
	if param.ChannelType != "" {
		_, ok := model.AllowModuleCode2[param.ChannelType]
		if !ok {
			ctx.JSON(200, gin.H{
				"code":    2,
				"message": "无效的参数 channel_type",
				"data": map[string]interface{}{
					"channel_type可选范围": "AI,AQ,DI,DQ",
				},
			})
			return
		}
		tx.Where("channel_type = ?", param.ChannelType)
	}

	if param.SpecType != "" {
		tx.Where("spec_type = ?", param.SpecType)
	}
	if param.ValueType != "" {
		tx.Where("value_type = ?", param.ValueType)
	}
	var count int64
	list := make([]*model.VariableModel, 0)
	err = tx.Count(&count).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}

	err = tx.Preload("PPEInfo").Preload("FSEInfo").Order("id ASC").Offset(param.StartPage()).Limit(param.PageSize).Find(&list).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}

	m := model.AlertTypeNameMap()
	for _, row := range list {
		row.AlertTypeName = m[int(row.AlertType)]
		if row.Code1 == "90" {
			row.Origin = fmt.Sprintf("%s", "内部变量")
			continue
		}
		if row.PPEInfo != nil {
			row.Origin = fmt.Sprintf("%s-%s", "第三方协议设备-", row.PPEInfo.Name)
			row.PPEInfo = nil
			continue
		}
		if row.FSEInfo != nil {
			row.Origin = fmt.Sprintf("%s-%s", "自研协议设备-", row.FSEInfo.Name)
			row.FSEInfo = nil
			continue
		}
		if row.Code1 == "90" {
			row.Origin = fmt.Sprintf("%s", "内部变量")
			continue
		}
		row.Origin = fmt.Sprintf("%s-%s", "", "")
	}
	for _, row := range list {
		if row.FSID > 0 {
			row.ModuleType = model.AllowFSCode2ModuleType[row.Code2]
		}
		if row.RCFormulaID > 0 {
			formula, ok := model.RCFormulaMap[row.RCFormulaID]
			if ok {
				row.RCFormulaName = formula.Name
			}
		}
		v, ok := model.VariablesMap.Get(row.Addr)
		if ok {
			row.Value = v.Value
		}
	}
	//jsonB, _ := json.Marshal(&list)
	ctx.JSON(200, gin.H{
		"code":    0,
		"message": "success",
		"data": map[string]interface{}{
			"list":  list,
			"count": count,
			//"json_detail": string(jsonB),
		},
	})
}

// Save 自研设备变量修改时需要同时新增或删除虚拟变量, 从AI,AQ改为DI,DQ时删除四个限制变量, 从DI,DQ改为AI,AQ时添加四个限制变量
func (con VariableController) Save(ctx *gin.Context) {
	userToken := GetUserToken(ctx)
	data := &model.Variable{}
	err := ctx.ShouldBindBodyWith(&data, binding.JSON)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的参数",
			"data":    err.Error(),
		})
		return
	}
	if data.RCFormulaID > 0 {
		_, ok := model.RCFormulaMap[data.RCFormulaID]
		if !ok {
			ctx.JSON(200, gin.H{
				"code":    2,
				"message": "无效的参数 rc_formula_id",
				"data":    map[string]interface{}{},
			})
		}
	}
	if data.RatioOperator == "" {
		data.RatioOperator = "*"
	}
	now := tools.GetNow()
	if data.ID == 0 {
		data.ProjectID = userToken.ProjectID
		data.Code1 = global.AppConfig.Section("internal_var").Key("code1").String()
		data.Code2 = global.AppConfig.Section("internal_var").Key("code2").String()
		data.Code3 = global.AppConfig.Section("internal_var").Key("code3").String()
		err = data.CheckParam("add", userToken.ProjectID) // 添加时校验参数. 顺便自动填充 code1~code3
		if err != nil {
			ctx.JSON(200, gin.H{
				"code":    2,
				"message": "参数校验失败 " + err.Error(),
				"data":    err.Error(),
			})
			return
		}
		usedCode4 := make([]string, 0)
		err = model.DB.Model(&model.FrameItem{}).Where("project_id = ? AND code1 = ? AND code2 = ? AND code3 = ?", userToken.ProjectID, data.Code1, data.Code2, data.Code3).Pluck("code4", &usedCode4).Error
		if err != nil {
			ctx.JSON(200, gin.H{
				"code":    6,
				"message": err.Error(),
			})
			return
		}
		availableCode4 := model.GetAvailableCode(model.AllCode4(), usedCode4)
		if len(availableCode4) < 1 {
			ctx.JSON(200, gin.H{
				"code":    1,
				"message": "当前模块下的变量号已满",
			})
			return
		}
		data.Code4 = availableCode4[0]
		data.Addr = fmt.Sprintf("%s-%s-%s-%s", data.Code1, data.Code2, data.Code3, data.Code4)
		data.CreateTime = &now
		data.UpdateTime = &now
		err = model.DB.Create(data).Error
	} else {
		data.UpdateTime = &now
		old := &model.Variable{}
		exist2, err2 := old.Exists(data.ID)
		if !exist2 {
			ctx.JSON(200, gin.H{
				"code":    2,
				"message": "无效的参数 id",
				"data": map[string]interface{}{
					"err": err2,
				},
			})
			return
		}
		err = data.CheckParam("update", userToken.ProjectID) // 添加时校验参数. 顺便自动填充 code1~code3
		if err != nil {
			ctx.JSON(200, gin.H{
				"code":    2,
				"message": "参数校验失败 " + err.Error(),
				"data":    err.Error(),
			})
			return
		}
		action := ""
		if old.FSID > 0 && old.Code2 == "04" {
			// 如果是Other 通道类型验证 module_type, 且修改后自动修改 data_type,spec_type
			switch data.ChannelType {
			case "AI":
				fallthrough
			case "AQ":
				data.SpecType = "float32"
				if old.ChannelType == "DI" || old.ChannelType == "DQ" {
					action = "add4"
				}
			case "DI":
				fallthrough
			case "DQ":
				data.SpecType = "uint8"
				if old.ChannelType == "AI" || old.ChannelType == "AQ" {
					action = "del4"
				}
			default:
				ctx.JSON(200, gin.H{
					"code":    2,
					"message": "Other通道变量的channel_type可选范围[AI,AQ,DI,DQ]",
					"data":    map[string]interface{}{},
				})
				return
			}
		} else {
			data.ChannelType = old.ChannelType
		}

		err = model.DB.Transaction(func(tx *gorm.DB) error {
			if old.FSID > 0 && old.Code2 == "04" {
				if action == "add4" {
					data.Upper2LimitVarAddr = old.Addr + ".upper2"
					data.UpperLimitVarAddr = old.Addr + ".upper"
					data.LowerLimitVarAddr = old.Addr + ".lower"
					data.Lower2LimitVarAddr = old.Addr + ".lower2"

					extendList := make([]*model.Variable, 0)
					row := old
					// 上上限
					tmpUpper2 := *row
					tmpUpper2.PID = row.ID
					tmpUpper2.ID = 0
					tmpUpper2.IsInternal = 1
					tmpUpper2.SpecType = "float32"
					tmpUpper2.Addr = row.Addr + ".upper2"
					tmpUpper2.FieldName = row.Addr + ".上上限"
					tmpUpper2.Value = row.Upper2LimitValue
					extendList = append(extendList, &tmpUpper2)
					// 上限
					tmpUpper := *row
					tmpUpper.PID = row.ID
					tmpUpper.ID = 0
					tmpUpper.IsInternal = 1
					tmpUpper.SpecType = "float32"
					tmpUpper.Addr = row.Addr + ".upper"
					tmpUpper.FieldName = row.Addr + ".上限"
					tmpUpper.Value = row.UpperLimitValue
					extendList = append(extendList, &tmpUpper)
					// 下限
					tmpLower := *row
					tmpLower.PID = row.ID
					tmpLower.ID = 0
					tmpLower.IsInternal = 1
					tmpLower.SpecType = "float32"
					tmpLower.Addr = row.Addr + ".lower"
					tmpLower.FieldName = row.Addr + ".下限"
					tmpLower.Value = row.LowerLimitValue
					extendList = append(extendList, &tmpLower)
					// 下下限
					tmpLower2 := *row
					tmpLower2.PID = row.ID
					tmpLower2.ID = 0
					tmpLower2.IsInternal = 1
					tmpLower2.SpecType = "float32"
					tmpLower2.Addr = row.Addr + ".lower2"
					tmpLower2.FieldName = row.Addr + ".下下限"
					tmpLower2.Value = row.Lower2LimitValue
					extendList = append(extendList, &tmpLower2)
					err = tx.Create(&extendList).Error
					if err != nil {
						return err
					}
				}

				if action == "del4" {
					data.Upper2LimitVarAddr = ""
					data.UpperLimitVarAddr = ""
					data.LowerLimitVarAddr = ""
					data.Lower2LimitVarAddr = ""

					addrs := []string{old.Addr + ".upper2", old.Addr + ".upper", old.Addr + ".lower", old.Addr + ".lower2"}
					err = model.DB.Where("project_id = ? AND addr in ?", old.ProjectID, addrs).Delete(&model.Variable{}).Error
					if err != nil {
						return err
					}
				}
			}
			err = tx.Model(&model.Variable{}).Where("id = ?", data.ID).Select("*").Omit("id", "project_id",
				"fs_id", "ppe_id", "frame_id", "module_id", "code1", "code2", "code3", "code4", "addr", "is_internal", "create_time").Updates(data).Error
			if err != nil {
				return err
			}
			return nil
		})

		if err == nil {
			v, ok := model.VariablesMap.Get(data.Addr)
			if ok {
				*v = *data
			}
			if data.FSID > 0 && data.ChannelType == "AI" {
				list := make([]*model.Data0605, 0)
				if old.UpperLimitValue != data.UpperLimitValue {
					list = append(list, &model.Data0605{LimitType: 0, Value: data.UpperLimitValue})
				}
				if old.LowerLimitValue != data.LowerLimitValue {
					list = append(list, &model.Data0605{LimitType: 1, Value: data.LowerLimitValue})
				}
				if old.Upper2LimitValue != data.Upper2LimitValue {
					list = append(list, &model.Data0605{LimitType: 2, Value: data.Upper2LimitValue})
				}
				if old.Lower2LimitValue != data.Lower2LimitValue {
					list = append(list, &model.Data0605{LimitType: 3, Value: data.Lower2LimitValue})
				}
				if len(list) > 0 {
					_ = model.UpdateAILimitValue(old.Addr, list)
				}
			}
			if data.FSID > 0 { // 更新 running_fse
				fse, ok2 := model.RunningFSMap.Get(data.FSID)
				j_log.LogInfo([]interface{}{"更新 running_fse", ok})
				if ok2 {
					for _, module := range fse.FSModule {
						for _, item := range module.FrameItem {
							if item.Addr == data.Addr {
								j_log.LogInfo([]interface{}{"running_fse已更新"})
								item = &((*data).FrameItem)
							}
						}
					}
				}
			}
		}
	}
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}

	ctx.JSON(200, gin.H{
		"code":    0,
		"message": "success",
		"data":    map[string]interface{}{},
	})
}

func (con VariableController) Detail(ctx *gin.Context) {
	param := struct {
		Id int `json:"id" binding:"required"`
	}{}
	if ctx.Request.Method == "POST" {
		err := ctx.ShouldBindBodyWith(&param, binding.JSON)
		if err != nil {
			ctx.JSON(200, gin.H{
				"code":    2,
				"message": "无效的参数",
				"data":    err.Error(),
			})
			return
		}
	} else {
		queryID := ctx.Query("id")
		param.Id, _ = strconv.Atoi(queryID)
	}

	info := model.Variable{}
	err := model.DB.Model(&model.Variable{}).Where("id = ?", param.Id).First(&info).Error

	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}

	info.Upper2, _ = model.GetVariableByAddr(info.ProjectID, info.Upper2LimitVarAddr)
	info.Upper, _ = model.GetVariableByAddr(info.ProjectID, info.UpperLimitVarAddr)
	info.Lower, _ = model.GetVariableByAddr(info.ProjectID, info.LowerLimitVarAddr)
	info.Lower2, _ = model.GetVariableByAddr(info.ProjectID, info.Lower2LimitVarAddr)

	ctx.JSON(200, gin.H{
		"code":    0,
		"message": "success",
		"data": map[string]interface{}{
			"info": info,
		},
	})
}

func (con VariableController) Delete(ctx *gin.Context) {
	param := struct {
		Id int `json:"id" binding:"required"`
	}{}
	err := ctx.ShouldBindBodyWith(&param, binding.JSON)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的参数",
			"data":    err.Error(),
		})
		return
	}
	info := model.Variable{}
	err = model.DB.Model(&model.Variable{}).Where("id = ?", param.Id).First(&info).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}
	err = model.DB.Transaction(func(tx *gorm.DB) error {
		err = tx.Where("id = ?", param.Id).Delete(&model.Variable{}).Error
		if err != nil {
			return err
		}
		err = tx.Where("pid = ?", param.Id).Delete(&model.Variable{}).Error
		if err != nil {
			return err
		}
		return nil
	})
	ctx.JSON(200, gin.H{
		"code":    0,
		"message": "success",
		"data":    map[string]interface{}{},
	})
}

func (con VariableController) MultiDelete(ctx *gin.Context) {
	param := struct {
		Ids string `json:"ids" binding:"required"`
	}{}
	err := ctx.ShouldBindBodyWith(&param, binding.JSON)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的参数",
			"data":    err.Error(),
		})
		return
	}
	idsArr := strings.Split(param.Ids, ",")
	ids := make([]int, 0)
	for _, str := range idsArr {
		id, _ := strconv.Atoi(str)
		if id != 0 {
			ids = append(ids, id)
		}
	}
	if len(ids) == 0 {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的参数 ids",
		})
		return
	}

	err = model.DB.Transaction(func(tx *gorm.DB) error {
		err = tx.Where("id in ?", ids).Delete(&model.Variable{}).Error
		if err != nil {
			return err
		}
		err = tx.Where("id in ?", ids).Delete(&model.Variable{}).Error
		if err != nil {
			return err
		}
		return nil
	})

	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}

	ctx.JSON(200, gin.H{
		"code":    0,
		"message": "success",
		"data":    map[string]interface{}{},
	})
}

func (con VariableController) AddrDetail(ctx *gin.Context) {
	userToken := GetUserToken(ctx)
	param := struct {
		Addr string `json:"addr" binding:"required,max=20"`
	}{}
	err := ctx.ShouldBindBodyWith(&param, binding.JSON)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的参数",
			"data":    err.Error(),
		})
		return
	}

	info := model.Variable{}
	err = model.DB.Model(&model.Variable{}).Where("project_id = ? AND addr = ?", userToken.ProjectID, param.Addr).First(&info).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}

	info.Upper2, _ = model.GetVariableByAddr(info.ProjectID, info.Upper2LimitVarAddr)
	info.Upper, _ = model.GetVariableByAddr(info.ProjectID, info.UpperLimitVarAddr)
	info.Lower, _ = model.GetVariableByAddr(info.ProjectID, info.LowerLimitVarAddr)
	info.Lower2, _ = model.GetVariableByAddr(info.ProjectID, info.Lower2LimitVarAddr)

	ctx.JSON(200, gin.H{
		"code":    0,
		"message": "success",
		"data": map[string]interface{}{
			"info": info,
		},
	})
}

func (con VariableController) LimitingValueUpdate(ctx *gin.Context) {
	userToken := GetUserToken(ctx)
	param := struct {
		Addr               string  `json:"addr" binding:"required,max=20"`
		Upper2LimitEnable  uint8   `json:"upper2_limit_enable" binding:"max=1"`
		Upper2LimitVarAddr string  `json:"upper2_limit_var_addr"`
		Upper2LimitValue   float64 `json:"upper2_limit_value"`
		UpperLimitEnable   uint8   `json:"upper_limit_enable" binding:"max=1"`
		UpperLimitVarAddr  string  `json:"upper_limit_var_addr"`
		UpperLimitValue    float64 `json:"upper_limit_value"`
		LowerLimitEnable   uint8   `json:"lower_limit_enable" binding:"max=1"`
		LowerLimitVarAddr  string  `json:"lower_limit_var_addr"`
		LowerLimitValue    float64 `json:"lower_limit_value"`
		Lower2LimitEnable  uint8   `json:"lower2_limit_enable" binding:"max=1"`
		Lower2LimitVarAddr string  `json:"lower2_limit_var_addr"`
		Lower2LimitValue   float64 `json:"lower2_limit_value"`
	}{}

	err := ctx.ShouldBindBodyWith(&param, binding.JSON)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的参数",
			"data":    err.Error(),
		})
		return
	}
	data := model.FrameItem{}
	err = model.DB.Model(&model.FrameItem{}).Where("project_id = ? AND addr = ?", userToken.ProjectID, param.Addr).Limit(1).Find(&data).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}
	if data.ID == 0 {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的 addr",
			"data":    "",
		})
		return
	}

	updateData := make(map[string]interface{})
	updateData["upper_limit_enable"] = param.UpperLimitEnable
	updateData["upper_limit_var_addr"] = param.UpperLimitVarAddr
	updateData["upper_limit_value"] = param.UpperLimitValue

	updateData["upper2_limit_enable"] = param.Upper2LimitEnable
	updateData["upper2_limit_var_addr"] = param.Upper2LimitVarAddr
	updateData["upper2_limit_value"] = param.Upper2LimitValue

	updateData["lower_limit_enable"] = param.LowerLimitEnable
	updateData["lower_limit_var_addr"] = param.LowerLimitVarAddr
	updateData["lower_limit_value"] = param.LowerLimitValue

	updateData["lower2_limit_enable"] = param.Lower2LimitEnable
	updateData["lower2_limit_var_addr"] = param.Lower2LimitVarAddr
	updateData["lower2_limit_value"] = param.Lower2LimitValue

	err = model.DB.Model(&model.FrameItem{}).Where("id = ?", data.ID).Updates(updateData).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}

	v, ok := model.VariablesMap.Get(param.Addr)
	if ok {
		v.UpperLimitEnable = param.UpperLimitEnable
		v.UpperLimitVarAddr = param.UpperLimitVarAddr
		v.UpperLimitValue = param.UpperLimitValue
		v.Upper2LimitEnable = param.Upper2LimitEnable
		v.Upper2LimitVarAddr = param.Upper2LimitVarAddr
		v.Upper2LimitValue = param.Upper2LimitValue
		v.LowerLimitEnable = param.LowerLimitEnable
		v.LowerLimitVarAddr = param.LowerLimitVarAddr
		v.LowerLimitValue = param.LowerLimitValue
		v.Lower2LimitEnable = param.Lower2LimitEnable
		v.Lower2LimitVarAddr = param.Lower2LimitVarAddr
		v.Lower2LimitValue = param.Lower2LimitValue
	}

	if data.FSID > 0 && data.ChannelType == "AI" {
		list := make([]*model.Data0605, 0)
		if param.UpperLimitValue != data.UpperLimitValue {
			list = append(list, &model.Data0605{LimitType: 0, Value: param.UpperLimitValue})
		}
		if param.LowerLimitValue != data.LowerLimitValue {
			list = append(list, &model.Data0605{LimitType: 1, Value: param.LowerLimitValue})
		}
		if param.Upper2LimitValue != data.Upper2LimitValue {
			list = append(list, &model.Data0605{LimitType: 2, Value: param.Upper2LimitValue})
		}
		if param.Lower2LimitValue != data.Lower2LimitValue {
			list = append(list, &model.Data0605{LimitType: 3, Value: param.Lower2LimitValue})
		}
		if len(list) > 0 {
			_ = model.UpdateAILimitValue(data.Addr, list)
		}
	}

	ctx.JSON(200, gin.H{
		"code":    0,
		"message": "success",
		"data":    map[string]interface{}{},
	})
}

func (con VariableController) LimitingEnable(ctx *gin.Context) {
	userToken := GetUserToken(ctx)
	param := struct {
		Addr              string `json:"addr" binding:"required,max=20"`
		AffectField       string `json:"affect_field" binding:"required,max=30"`
		UpperLimitEnable  uint8  `json:"upper_limit_enable" binding:"max=1"`
		Upper2LimitEnable uint8  `json:"upper2_limit_enable" binding:"max=1"`
		LowerLimitEnable  uint8  `json:"lower_limit_enable" binding:"max=1"`
		Lower2LimitEnable uint8  `json:"lower2_limit_enable" binding:"max=1"`
	}{}

	err := ctx.ShouldBindBodyWith(&param, binding.JSON)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的参数",
			"data":    err.Error(),
		})
		return
	}
	info := model.FrameItem{}
	err = model.DB.Model(&model.FrameItem{}).Where("project_id = ? AND addr = ?", userToken.ProjectID, param.Addr).Limit(1).Find(&info).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}
	if info.ID == 0 {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的 addr",
			"data":    "",
		})
		return
	}

	updateData := make(map[string]interface{})
	if param.AffectField == "upper_limit_enable" {
		updateData["upper_limit_enable"] = param.UpperLimitEnable
	} else if param.AffectField == "upper2_limit_enable" {
		updateData["upper2_limit_enable"] = param.Upper2LimitEnable
	} else if param.AffectField == "lower_limit_enable" {
		updateData["lower_limit_enable"] = param.LowerLimitEnable
	} else if param.AffectField == "lower2_limit_enable" {
		updateData["lower2_limit_enable"] = param.Lower2LimitEnable
	} else {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的参数 affect_field",
			"data":    "",
		})
		return
	}

	err = model.DB.Model(&model.FrameItem{}).Where("id = ?", info.ID).Updates(updateData).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}
	// 修改内存中的限值数据
	v, ok := model.VariablesMap.Get(param.Addr)
	if ok {
		if param.AffectField == "upper_limit_enable" {
			v.UpperLimitEnable = param.UpperLimitEnable
		} else if param.AffectField == "upper2_limit_enable" {
			v.Upper2LimitEnable = param.Upper2LimitEnable
		} else if param.AffectField == "lower_limit_enable" {
			v.LowerLimitEnable = param.LowerLimitEnable
		} else if param.AffectField == "lower2_limit_enable" {
			v.Lower2LimitEnable = param.Lower2LimitEnable
		}
	}

	ctx.JSON(200, gin.H{
		"code":    0,
		"message": "success",
		"data":    map[string]interface{}{},
	})
}

func (con VariableController) EVUpdate(ctx *gin.Context) {
	userToken := GetUserToken(ctx)
	param := struct {
		Content string `json:"content" binding:"required,max=20000"` // 模型内容 json字符串格式
	}{}
	err := ctx.ShouldBindBodyWith(&param, binding.JSON)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的参数",
			"data":    err.Error(),
		})
		return
	}
	list := make([]*model.FrameItem, 0)
	err = json.Unmarshal([]byte(param.Content), &list)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "json解析失败 content",
			"data":    err.Error(),
		})
		return
	}

	now := tools.GetNow()
	for _, row := range list {
		row.UpdateTime = &now
		err = model.DB.Model(&model.FrameItem{}).Where("project_id = ? AND addr = ?", userToken.ProjectID, row.Addr).Select("ev", "update_time").Updates(row).Error
		if err != nil {
			ctx.JSON(200, gin.H{
				"code":    6,
				"message": err.Error(),
				"data": map[string]interface{}{
					"row": row,
				},
			})
			return
		}
	}
	ctx.JSON(200, gin.H{
		"code":    0,
		"message": "success",
		"data":    map[string]interface{}{},
	})
}

func (con VariableController) RCFormulaList(ctx *gin.Context) {
	list := make([]*model.RCFormula, 0)
	err := model.DB.Order("id ASC").Offset(0).Limit(1000).Find(&list).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}
	ctx.JSON(200, gin.H{
		"code":    0,
		"message": "success",
		"data": map[string]interface{}{
			"list": list,
		},
	})
}

func (con VariableController) MemValueUpdate(ctx *gin.Context) {
	userToken := GetUserToken(ctx)
	param := struct {
		Addr     string  `json:"addr" binding:"required,max=20"`
		Value    float64 `json:"value"`
		StrValue string  `json:"str_value"`
	}{}
	err := ctx.ShouldBindBodyWith(&param, binding.JSON)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的参数",
			"data":    err.Error(),
		})
		return
	}
	info := &model.Variable{}
	err = model.DB.Model(&model.Variable{}).Where("project_id = ? AND addr = ?", userToken.ProjectID, param.Addr).First(&info).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}

	if info.SpecType != "string" {
		updateData := map[string]interface{}{
			"value": param.Value,
		}
		err = model.DB.Model(&model.Variable{}).Where("id = ?", info.ID).Updates(updateData).Error
		if err != nil {
			ctx.JSON(200, gin.H{
				"code":    6,
				"message": err.Error(),
			})
			return
		}
	}

	exist, ok := model.VariablesMap.Get(info.Addr)
	if ok {
		if info.SpecType != "string" {
			exist.Value = param.Value
		} else {
			exist.ValueStr = param.StrValue
		}
	} else {
		if info.SpecType != "string" {
			info.Value = param.Value
		} else {
			info.ValueStr = param.StrValue
		}
		model.VariablesMap.Set(info.Addr, info)
	}

	err = model.WriteVariablesToModbusPoint(info.Addr, param.Value)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    1,
			"message": "写入Modbus点位值失败",
			"data":    err.Error(),
		})
		return
	}

	ctx.JSON(200, gin.H{
		"code":    0,
		"message": "success",
		"data":    map[string]interface{}{},
	})

}

func (con VariableController) MemValueRead(ctx *gin.Context) {
	param := struct {
		Addr string `json:"addr" binding:"required,max=20"`
	}{}
	err := ctx.ShouldBindBodyWith(&param, binding.JSON)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的参数",
			"data":    err.Error(),
		})
		return
	}
	v, ok := model.VariablesMap.Get(param.Addr)
	if !ok {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "未找到当前变量",
		})
		return
	}
	ctx.JSON(200, gin.H{
		"code":    0,
		"message": "success",
		"data": map[string]interface{}{
			"info": v,
		},
	})

}

// VariableWrite 根据变量地址写入变量值
func (con VariableController) Write(ctx *gin.Context) {
	userToken := GetUserToken(ctx)
	param := struct {
		Addr  string  `json:"addr"`
		Value float64 `json:"value"`
	}{}
	err := ctx.ShouldBindBodyWith(&param, binding.JSON)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的参数",
			"data":    err.Error(),
		})
		return
	}
	if param.Addr == "" {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "变量未连接",
			"data":    map[string]interface{}{},
		})
		return
	}
	variable := &model.Variable{}
	err = model.DB.Model(&model.Variable{}).Where("project_id = ? AND addr = ?", userToken.ProjectID, param.Addr).Limit(1).Find(&variable).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}
	if variable.ID == 0 {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的 addr",
			"data":    "",
		})
		return
	}
	model.VariablesMap.PushMessageToClients([]*model.QueueValue{
		{
			Addr:  variable.Addr,
			Value: param.Value,
		},
	})
	opLog := model.OperationLog{
		ProjectID: userToken.ProjectID,
		UID:       userToken.Uid,
		Type:      2,
		Desc:      fmt.Sprintf("%s(%s) 写入值 %v", variable.FieldName, variable.Addr, param.Value),
	}
	_ = opLog.Log()
	// 1. 先把值写入内存
	exist, ok := model.VariablesMap.Get(variable.Addr)
	if ok {
		exist.Value = param.Value
	} else {
		variable.Value = param.Value
		model.VariablesMap.Set(variable.Addr, variable)
	}

	/*--- OPC专用 start ---*/
	val := ""
	switch variable.SpecType {
	case "bool":
		if param.Value == 0 {
			val = "0"
		} else {
			val = "1"
		}
	case "uint8":
		val = fmt.Sprintf("%d", uint8(param.Value))
	case "int8":
		val = fmt.Sprintf("%d", int8(uint8(param.Value)))
	case "uint16":
		val = fmt.Sprintf("%d", uint16(param.Value))
	case "int16":
		val = fmt.Sprintf("%d", int16(uint16(param.Value)))
	case "uint32":
		val = fmt.Sprintf("%d", uint32(param.Value))
	case "int32":
		val = fmt.Sprintf("%d", int32(uint32(param.Value)))
	case "float32":
		val = fmt.Sprintf("%f", float32(param.Value))
	case "float64":
		val = fmt.Sprintf("%v", param.Value)
	}
	/*--- OPC专用 end ---*/
	// 2. 再把值按指定协议发送至设备
	rawData := ""               // TCP,RS485,RS232 数据包(十六进制格式)
	if variable.Code1 == "58" { // OPC Client
		req := model.OPCWSRequest{
			Route: "opcua/write",
			TS:    tools.GetNow(),
			Params: map[string]interface{}{
				"ppe_id": variable.PPEID,
				"addr":   variable.OPCAddr,
				"val":    val,
			},
		}
		jsonB, _ := json.Marshal(&req)
		if model.OPCWSClient == nil {
			ctx.JSON(200, gin.H{
				"code":    1,
				"message": fmt.Sprintf("OPCWSClient 未连接"),
				"data": map[string]interface{}{
					"rawData": string(jsonB),
				},
			})
			return
		} else {
			err = model.OPCWSClient.WriteMessage(websocket.TextMessage, jsonB)
			if err != nil {
				ctx.JSON(200, gin.H{
					"code":    1,
					"message": fmt.Sprintf("OPCWSClient.WriteMessage error %s", err.Error()),
					"rawData": string(jsonB),
				})
				return
			}
			rawData = string(jsonB)
		}
	} else if variable.Code1 == "61" { // OPC DA Client
		req := model.OPCWSRequest{
			Route: "opcda/write",
			TS:    tools.GetNow(),
			Params: map[string]interface{}{
				"ppe_id": variable.PPEID,
				"addr":   variable.OPCAddr,
				"val":    val,
			},
		}
		jsonB, _ := json.Marshal(&req)
		if model.OPCWSClient == nil {
			ctx.JSON(200, gin.H{
				"code":    1,
				"message": fmt.Sprintf("OPCWSClient 未连接"),
				"data": map[string]interface{}{
					"rawData": string(jsonB),
				},
			})
			return
		} else {
			err = model.OPCWSClient.WriteMessage(websocket.TextMessage, jsonB)
			if err != nil {
				ctx.JSON(200, gin.H{
					"code":    1,
					"message": fmt.Sprintf("OPCWSClient.WriteMessage error %s", err.Error()),
					"rawData": string(jsonB),
				})
				return
			}
			rawData = string(jsonB)
		}
	} else if variable.FSID > 0 { // 自研设备
		resB, err := model.UpdateAQDQValueSingle(variable, param.Addr, param.Value)
		if err != nil {
			ctx.JSON(200, gin.H{
				"code":    1,
				"message": err.Error(),
			})
			return
		}
		rawData = tools.ByteArrayToHexStr(resB)
	} else if variable.FrameID > 0 {
		frame := &model.Frame{}
		err = model.DB.Where("id = ?", variable.FrameID).Limit(1).Find(frame).Error
		if err != nil {
			ctx.JSON(200, gin.H{
				"code":    6,
				"message": err.Error(),
			})
			return
		}
		if frame.ID == 0 {
			ctx.JSON(200, gin.H{
				"code":    2,
				"message": "未找到当前变量的相关帧信息",
				"data":    "",
			})
			return
		}
		err, rawDataB := frame.Send()
		if err != nil {
			ctx.JSON(200, gin.H{
				"code":    1,
				"message": err.Error(),
				"data":    map[string]interface{}{},
			})
			return
		}
		rawData = tools.ByteArrayToHexStr(rawDataB)
	}

	err = model.WriteVariablesToModbusPoint(variable.Addr, param.Value)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    1,
			"message": "modbus server 写入点位值失败",
			"data":    err.Error(),
		})
		return
	}

	ctx.JSON(200, gin.H{
		"code":    0,
		"message": "success",
		"data": map[string]interface{}{
			"rawData": rawData,
		},
	})
}

type VariableWriteStu struct {
	Addr    string  `json:"addr"`
	Value   float64 `json:"value"`
	Result  string  `json:"result"`
	RawData string  `json:"raw_data"`
}

func (con VariableController) WriteMulti(ctx *gin.Context) {
	userToken := GetUserToken(ctx)
	body := struct {
		Content string `json:"content" binding:"required"`
	}{}
	err := ctx.ShouldBindBodyWith(&body, binding.JSON)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的参数 content",
			"data":    err.Error(),
		})
		return
	}
	content := make([]*VariableWriteStu, 0)
	err = json.Unmarshal([]byte(body.Content), &content)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "content 参数解析失败",
			"data":    err.Error(),
		})
		return
	}
	emptyAddr := false
	for _, param := range content {
		if param.Addr == "" {
			emptyAddr = true
			break
		}
	}
	if emptyAddr {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "有未连接变量",
			"data":    map[string]interface{}{},
		})
		return
	}
	for _, param := range content {
		param.Result = "success"
		variable := &model.Variable{}
		err = model.DB.Model(&model.Variable{}).Where("project_id = ? AND addr = ?", userToken.ProjectID, param.Addr).Limit(1).Find(&variable).Error
		if err != nil {
			param.Result = err.Error()
			continue
		}
		if variable.ID == 0 {
			param.Result = "无效的 addr"
			continue
		}
		model.VariablesMap.PushMessageToClients([]*model.QueueValue{
			{
				Addr:  param.Addr,
				Value: param.Value,
			},
		})
		// 1. 先把值写入内存
		exist, ok := model.VariablesMap.Get(variable.Addr)
		if ok {
			exist.Value = param.Value
		} else {
			variable.Value = param.Value
			model.VariablesMap.Set(variable.Addr, variable)
		}
		/*--- OPC专用 start ---*/
		val := ""
		switch variable.SpecType {
		case "bool":
			if param.Value == 0 {
				val = "0"
			} else {
				val = "1"
			}
		case "uint8":
			val = fmt.Sprintf("%d", uint8(param.Value))
		case "int8":
			val = fmt.Sprintf("%d", int8(uint8(param.Value)))
		case "uint16":
			val = fmt.Sprintf("%d", uint16(param.Value))
		case "int16":
			val = fmt.Sprintf("%d", int16(uint16(param.Value)))
		case "uint32":
			val = fmt.Sprintf("%d", uint32(param.Value))
		case "int32":
			val = fmt.Sprintf("%d", int32(uint32(param.Value)))
		case "float32":
			val = fmt.Sprintf("%f", float32(param.Value))
		case "float64":
			val = fmt.Sprintf("%v", param.Value)
		}

		opLog := model.OperationLog{
			ProjectID: userToken.ProjectID,
			UID:       userToken.Uid,
			Type:      2,
			Desc:      fmt.Sprintf("%s(%s) 写入值 %v", variable.FieldName, variable.Addr, param.Value),
		}
		_ = opLog.Log()

		/*--- OPC专用 end ---*/
		// 2. 再把值按指定协议发送至设备
		if variable.Code1 == "58" { // OPC Client
			req := model.OPCWSRequest{
				Route: "opcua/write",
				TS:    tools.GetNow(),
				Params: map[string]interface{}{
					"ppe_id": variable.PPEID,
					"addr":   variable.OPCAddr,
					"val":    val,
				},
			}
			jsonB, _ := json.Marshal(&req)
			if model.OPCWSClient == nil {
				param.Result = "OPCWSClient 未连接"
				continue
			} else {
				err = model.OPCWSClient.WriteMessage(websocket.TextMessage, jsonB)
				if err != nil {
					param.Result = fmt.Sprintf("OPCWSClient.WriteMessage error %s", err.Error())
					continue
				}
				param.RawData = string(jsonB)
			}
		} else if variable.Code1 == "61" { // OPC DA Client
			req := model.OPCWSRequest{
				Route: "opcda/write",
				TS:    tools.GetNow(),
				Params: map[string]interface{}{
					"ppe_id": variable.PPEID,
					"addr":   variable.OPCAddr,
					"val":    fmt.Sprintf("%f", param.Value),
				},
			}
			jsonB, _ := json.Marshal(&req)
			if model.OPCWSClient == nil {
				param.Result = "OPCWSClient 未连接"
				continue
			} else {
				err = model.OPCWSClient.WriteMessage(websocket.TextMessage, jsonB)
				if err != nil {
					param.Result = fmt.Sprintf("OPCWSClient.WriteMessage error %s", err.Error())
					continue
				}
				param.RawData = string(jsonB)
			}
		} else if variable.FSID > 0 { // 自研设备
			resB, err := model.UpdateAQDQValueSingle(variable, param.Addr, param.Value)
			if err != nil {
				param.Result = fmt.Sprintf("UpdateAQDQValueSingle error %s", err.Error())
				continue
			}
			param.RawData = tools.ByteArrayToHexStr(resB)
		} else if variable.FrameID > 0 {
			// todo 持续开发中
			frame := &model.Frame{}
			err = model.DB.Where("id = ?", variable.FrameID).Limit(1).Find(frame).Error
			if err != nil {
				param.Result = fmt.Sprintf("sql error %s", err.Error())
				continue
			}
			if frame.ID == 0 {
				param.Result = fmt.Sprintf("未找到当前变量的相关帧信息")
				continue
			}
			err, rawDataB := frame.Send()
			if err != nil {
				ctx.JSON(200, gin.H{
					"code":    1,
					"message": err.Error(),
					"data":    map[string]interface{}{},
				})
				return
			}
			param.RawData = tools.ByteArrayToHexStr(rawDataB)
		}
		err = model.WriteVariablesToModbusPoint(variable.Addr, param.Value)
		if err != nil {
			param.Result = fmt.Sprintf("modbus server写入点位值失败")
			continue
		}
	}

	ctx.JSON(200, gin.H{
		"code":    0,
		"message": "success",
		"data": map[string]interface{}{
			"content": content,
		},
	})
}

// LimitingValueUpdateSingle 变量限值修改; 如果是自研设备同时下发值到设备(仅限AI和Other中的AI)
func (con VariableController) LimitingValueUpdateSingle(ctx *gin.Context) {
	userToken := GetUserToken(ctx)
	param := struct {
		Addr  string  `json:"addr" binding:"required,max=20"`
		Type  string  `json:"type" binding:"required"` // 限值类型 [upper2,upper,lower,lower2]
		Value float64 `json:"value"`
	}{}
	err := ctx.ShouldBindBodyWith(&param, binding.JSON)
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的参数",
			"data":    err.Error(),
		})
		return
	}
	variable := &model.Variable{}
	err = model.DB.Model(&model.Variable{}).Where("project_id = ? AND addr = ?", userToken.ProjectID, param.Addr).Limit(1).Find(variable).Error
	if err != nil {
		ctx.JSON(200, gin.H{
			"code":    6,
			"message": err.Error(),
		})
		return
	}
	if variable.ID == 0 {
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的 addr",
			"data":    "",
		})
		return
	}
	exist, ok := model.VariablesMap.Get(variable.Addr)
	list := make([]*model.Data0605, 0)
	switch param.Type {
	case "upper2":
		if ok {
			exist.Upper2LimitValue = param.Value
		} else {
			variable.Upper2LimitValue = param.Value
			model.VariablesMap.Set(variable.Addr, variable)
		}
		_ = model.DB.Model(&model.Variable{}).Where("id = ?", variable.ID).Update("upper2_limit_value", param.Value).Error
		list = append(list, &model.Data0605{LimitType: 2, Value: param.Value})
	case "upper":
		if ok {
			exist.UpperLimitValue = param.Value
		} else {
			variable.UpperLimitValue = param.Value
			model.VariablesMap.Set(variable.Addr, variable)
		}
		_ = model.DB.Model(&model.Variable{}).Where("id = ?", variable.ID).Update("upper_limit_value", param.Value).Error
		list = append(list, &model.Data0605{LimitType: 0, Value: param.Value})
	case "lower":
		if ok {
			exist.LowerLimitValue = param.Value
		} else {
			variable.LowerLimitValue = param.Value
			model.VariablesMap.Set(variable.Addr, variable)
		}
		_ = model.DB.Model(&model.Variable{}).Where("id = ?", variable.ID).Update("lower_limit_value", param.Value).Error
		list = append(list, &model.Data0605{LimitType: 1, Value: param.Value})
	case "lower2":
		if ok {
			exist.Lower2LimitValue = param.Value
		} else {
			variable.Lower2LimitValue = param.Value
			model.VariablesMap.Set(variable.Addr, variable)
		}
		_ = model.DB.Model(&model.Variable{}).Where("id = ?", variable.ID).Update("lower2_limit_value", param.Value).Error
		list = append(list, &model.Data0605{LimitType: 3, Value: param.Value})
	default:
		ctx.JSON(200, gin.H{
			"code":    2,
			"message": "无效的限值类型",
			"data":    "type可选范围[upper2,upper,lower,lower2]",
		})
		return
	}
	if len(list) > 0 {
		_ = model.UpdateAILimitValue(param.Addr, list)
	}
	ctx.JSON(200, gin.H{
		"code":    0,
		"message": "success",
		"data":    map[string]interface{}{},
	})
}
